{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "e371ea2b",
   "metadata": {},
   "source": [
    "# YouTube Video Summarizer\n",
    "\n",
    "This notebook provides a comprehensive solution for summarizing YouTube videos using OpenAI's GPT models. It includes:\n",
    "\n",
    "- **Automatic transcript extraction** from YouTube videos\n",
    "- **Intelligent chunking** for large videos that exceed token limits\n",
    "- **Smart summarization** with academic-quality output\n",
    "- **Error handling** and dependency management\n",
    "\n",
    "## Features\n",
    "\n",
    "- ✅ Extracts transcripts from YouTube videos\n",
    "- ✅ Handles videos of any length with automatic chunking\n",
    "- ✅ Generates structured, academic-quality summaries\n",
    "- ✅ Includes proper error handling and dependency checks\n",
    "- ✅ Optimized for different OpenAI models\n",
    "- ✅ Interactive notebook format for easy testing\n",
    "\n",
    "## Prerequisites\n",
    "\n",
    "Make sure you have the required dependencies installed:\n",
    "```bash\n",
    "pip install -r requirements.txt\n",
    "```\n",
    "\n",
    "You'll also need an OpenAI API key set in your environment variables or `.env` file.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95b713e0",
   "metadata": {},
   "source": [
    "## 1. Import Dependencies and Setup\n",
    "\n",
    "First, let's import all required libraries and set up the environment.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c940970b",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import re\n",
    "import sys\n",
    "\n",
    "# Check for required dependencies and provide helpful error messages\n",
    "try:\n",
    "    import requests\n",
    "    print(\"✅ requests imported successfully\")\n",
    "except ImportError:\n",
    "    print(\"❌ Error: 'requests' module not found.\")\n",
    "    print(\"💡 Install with: pip install requests\")\n",
    "    print(\"   Or: pip install -r requirements.txt\")\n",
    "    sys.exit(1)\n",
    "\n",
    "try:\n",
    "    import tiktoken\n",
    "    print(\"✅ tiktoken imported successfully\")\n",
    "except ImportError:\n",
    "    print(\"❌ Error: 'tiktoken' module not found.\")\n",
    "    print(\"💡 Install with: pip install tiktoken\")\n",
    "    print(\"   Or: pip install -r requirements.txt\")\n",
    "    sys.exit(1)\n",
    "\n",
    "try:\n",
    "    from dotenv import load_dotenv\n",
    "    print(\"✅ python-dotenv imported successfully\")\n",
    "except ImportError:\n",
    "    print(\"❌ Error: 'python-dotenv' module not found.\")\n",
    "    print(\"💡 Install with: pip install python-dotenv\")\n",
    "    print(\"   Or: pip install -r requirements.txt\")\n",
    "    sys.exit(1)\n",
    "\n",
    "try:\n",
    "    from openai import OpenAI\n",
    "    print(\"✅ openai imported successfully\")\n",
    "except ImportError:\n",
    "    print(\"❌ Error: 'openai' module not found.\")\n",
    "    print(\"💡 Install with: pip install openai\")\n",
    "    print(\"   Or: pip install -r requirements.txt\")\n",
    "    sys.exit(1)\n",
    "\n",
    "try:\n",
    "    from youtube_transcript_api import YouTubeTranscriptApi\n",
    "    print(\"✅ youtube-transcript-api imported successfully\")\n",
    "except ImportError:\n",
    "    print(\"❌ Error: 'youtube-transcript-api' module not found.\")\n",
    "    print(\"💡 Install with: pip install youtube-transcript-api\")\n",
    "    print(\"   Or: pip install -r requirements.txt\")\n",
    "    sys.exit(1)\n",
    "\n",
    "try:\n",
    "    from bs4 import BeautifulSoup\n",
    "    print(\"✅ beautifulsoup4 imported successfully\")\n",
    "except ImportError:\n",
    "    print(\"❌ Error: 'beautifulsoup4' module not found.\")\n",
    "    print(\"💡 Install with: pip install beautifulsoup4\")\n",
    "    print(\"   Or: pip install -r requirements.txt\")\n",
    "    sys.exit(1)\n",
    "\n",
    "try:\n",
    "    from IPython.display import Markdown, display\n",
    "    print(\"✅ IPython.display imported successfully\")\n",
    "except ImportError:\n",
    "    # IPython is optional for Jupyter notebooks\n",
    "    print(\"⚠️  Warning: IPython not available (optional for Jupyter notebooks)\")\n",
    "    Markdown = None\n",
    "    display = None\n",
    "\n",
    "print(\"\\n🎉 All dependencies imported successfully!\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "603e9c3b",
   "metadata": {},
   "source": [
    "## 2. Configuration and Constants\n",
    "\n",
    "Set up headers for web scraping and define the YouTubeVideo class.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8584ca1a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Headers for website scraping\n",
    "headers = {\n",
    "    \"User-Agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36\"\n",
    "}\n",
    "\n",
    "class YouTubeVideo:\n",
    "    \"\"\"Class to handle YouTube video data extraction and processing\"\"\"\n",
    "    \n",
    "    def __init__(self, url):\n",
    "        \"\"\"\n",
    "        Initialize YouTube video object\n",
    "        \n",
    "        Args:\n",
    "            url (str): YouTube video URL\n",
    "        \"\"\"\n",
    "        self.url = url\n",
    "        youtube_pattern = r'https://www\\.youtube\\.com/watch\\?v=[a-zA-Z0-9_-]+'\n",
    "        \n",
    "        if re.match(youtube_pattern, url):\n",
    "            response = requests.get(url, headers=headers)\n",
    "            soup = BeautifulSoup(response.content, 'html.parser')\n",
    "            self.video_id = url.split(\"v=\")[1]\n",
    "            self.title = soup.title.string if soup.title else \"No title found\"\n",
    "            self.transcript = YouTubeTranscriptApi().fetch(self.video_id)\n",
    "        else:\n",
    "            raise ValueError(\"Invalid YouTube URL\")\n",
    "    \n",
    "    def get_transcript_text(self):\n",
    "        \"\"\"Get transcript as a single text string\"\"\"\n",
    "        return \" \".join([segment.text for segment in self.transcript])\n",
    "    \n",
    "    def get_video_info(self):\n",
    "        \"\"\"Get basic video information\"\"\"\n",
    "        return {\n",
    "            \"title\": self.title,\n",
    "            \"video_id\": self.video_id,\n",
    "            \"url\": self.url,\n",
    "            \"transcript_length\": len(self.transcript)\n",
    "        }\n",
    "\n",
    "print(\"✅ YouTubeVideo class defined successfully\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "235e9998",
   "metadata": {},
   "source": [
    "## 3. OpenAI API Setup\n",
    "\n",
    "Functions to handle OpenAI API key and client initialization.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4fa7aba3",
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_api_key():\n",
    "    \"\"\"Get OpenAI API key from environment variables\"\"\"\n",
    "    load_dotenv(override=True)\n",
    "    api_key = os.getenv(\"OPENAI_API_KEY\")\n",
    "    if not api_key:\n",
    "        raise ValueError(\"OPENAI_API_KEY is not set. Please set it in your environment variables or .env file.\")\n",
    "    return api_key\n",
    "\n",
    "def get_openai_client():\n",
    "    \"\"\"Initialize and return OpenAI client\"\"\"\n",
    "    api_key = get_api_key()\n",
    "    return OpenAI(api_key=api_key)\n",
    "\n",
    "# Test API connection\n",
    "try:\n",
    "    client = get_openai_client()\n",
    "    print(\"✅ OpenAI client initialized successfully\")\n",
    "    print(\"✅ API key is valid\")\n",
    "except Exception as e:\n",
    "    print(f\"❌ Error initializing OpenAI client: {e}\")\n",
    "    print(\"💡 Make sure you have set your OPENAI_API_KEY environment variable\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d3223f4",
   "metadata": {},
   "source": [
    "## 4. Token Counting and Chunking Functions\n",
    "\n",
    "Functions to handle token counting and intelligent chunking of large transcripts.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "71f68ad0",
   "metadata": {},
   "outputs": [],
   "source": [
    "def count_tokens(text, model=\"gpt-4o-mini\"):\n",
    "    \"\"\"Count tokens in text using tiktoken with fallback\"\"\"\n",
    "    try:\n",
    "        # Try model-specific encoding first\n",
    "        encoding = tiktoken.encoding_for_model(model)\n",
    "        return len(encoding.encode(text))\n",
    "    except KeyError:\n",
    "        # Fallback to cl100k_base encoding (used by most OpenAI models)\n",
    "        # This ensures compatibility even if model-specific encoding isn't available\n",
    "        encoding = tiktoken.get_encoding(\"cl100k_base\")\n",
    "        return len(encoding.encode(text))\n",
    "    except Exception as e:\n",
    "        # Ultimate fallback - rough estimation\n",
    "        print(f\"Warning: Token counting failed ({e}), using rough estimation\")\n",
    "        return len(text.split()) * 1.3  # Rough word-to-token ratio\n",
    "\n",
    "def get_optimal_chunk_size(model=\"gpt-4o-mini\"):\n",
    "    \"\"\"Calculate optimal chunk size based on model's context window\"\"\"\n",
    "    model_limits = {\n",
    "        \"gpt-4o-mini\": 8192,\n",
    "        \"gpt-4o\": 128000,\n",
    "        \"gpt-4-turbo\": 128000,\n",
    "        \"gpt-3.5-turbo\": 4096,\n",
    "        \"gpt-4\": 8192,\n",
    "    }\n",
    "    \n",
    "    context_window = model_limits.get(model, 8192)  # Default to 8K\n",
    "    \n",
    "    # Reserve tokens for:\n",
    "    # - System prompt: ~800 tokens\n",
    "    # - User prompt overhead: ~300 tokens  \n",
    "    # - Output: ~2000 tokens\n",
    "    # - Safety buffer: ~500 tokens\n",
    "    reserved_tokens = 800 + 300 + 2000 + 500\n",
    "    \n",
    "    optimal_chunk_size = context_window - reserved_tokens\n",
    "    \n",
    "    # Ensure minimum chunk size\n",
    "    return max(optimal_chunk_size, 2000)\n",
    "\n",
    "print(\"✅ Token counting and chunk size functions defined\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b6647838",
   "metadata": {},
   "outputs": [],
   "source": [
    "def chunk_transcript(transcript, max_tokens=4000, overlap_tokens=200, model=\"gpt-4o-mini\"):\n",
    "    \"\"\"\n",
    "    Split transcript into chunks that fit within token limits\n",
    "    \n",
    "    Args:\n",
    "        transcript: List of transcript segments from YouTube\n",
    "        max_tokens: Maximum tokens per chunk (auto-calculated if None)\n",
    "        overlap_tokens: Number of tokens to overlap between chunks\n",
    "        model: Model name for token limit calculation\n",
    "    \n",
    "    Returns:\n",
    "        List of transcript chunks\n",
    "    \"\"\"\n",
    "    # Auto-calculate max_tokens based on model if not provided\n",
    "    if max_tokens is None:\n",
    "        max_tokens = get_optimal_chunk_size(model)\n",
    "    \n",
    "    # Auto-calculate overlap as percentage of max_tokens\n",
    "    if overlap_tokens is None:\n",
    "        overlap_tokens = int(max_tokens * 0.05)  # 5% overlap\n",
    "        \n",
    "    # Convert transcript to text\n",
    "    transcript_text = \" \".join([segment.text for segment in transcript])\n",
    "    \n",
    "    # If transcript is small enough, return as single chunk\n",
    "    if count_tokens(transcript_text) <= max_tokens:\n",
    "        return [transcript_text]\n",
    "    \n",
    "    # Split into sentences for better chunking\n",
    "    sentences = re.split(r'[.!?]+', transcript_text)\n",
    "    chunks = []\n",
    "    current_chunk = \"\"\n",
    "    \n",
    "    for sentence in sentences:\n",
    "        sentence = sentence.strip()\n",
    "        if not sentence:\n",
    "            continue\n",
    "            \n",
    "        # Check if adding this sentence would exceed token limit\n",
    "        test_chunk = current_chunk + \" \" + sentence if current_chunk else sentence\n",
    "        \n",
    "        if count_tokens(test_chunk) <= max_tokens:\n",
    "            current_chunk = test_chunk\n",
    "        else:\n",
    "            # Save current chunk and start new one\n",
    "            if current_chunk:\n",
    "                chunks.append(current_chunk)\n",
    "            \n",
    "            # Start new chunk with overlap from previous chunk\n",
    "            if chunks and overlap_tokens > 0:\n",
    "                # Get last few words from previous chunk for overlap\n",
    "                prev_words = current_chunk.split()[-overlap_tokens//4:]  # Rough word-to-token ratio\n",
    "                current_chunk = \" \".join(prev_words) + \" \" + sentence\n",
    "            else:\n",
    "                current_chunk = sentence\n",
    "    \n",
    "    # Add the last chunk\n",
    "    if current_chunk:\n",
    "        chunks.append(current_chunk)\n",
    "    \n",
    "    return chunks\n",
    "\n",
    "print(\"✅ Chunking function defined\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7ee3f8a4",
   "metadata": {},
   "source": [
    "## 5. Prompt Generation Functions\n",
    "\n",
    "Functions to generate system prompts, user prompts, and stitching prompts for the summarization process.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e7f20bf5",
   "metadata": {},
   "outputs": [],
   "source": [
    "def generate_system_prompt():\n",
    "    \"\"\"Generate the system prompt for video summarization\"\"\"\n",
    "    return f\"\"\"\n",
    "    You are an expert YouTube video summarizer. Your job is to take the full transcript of a video and generate a structured, precise, and academically grounded summary.\n",
    "\n",
    "    Your output must include:\n",
    "\n",
    "    1. Title\n",
    "    - Either reuse the video's title (if it is clear, accurate, and concise)\n",
    "    - Or generate a new, sharper, more descriptive title that best reflects the actual content covered.\n",
    "\n",
    "    2. Topic & Area of Coverage\n",
    "    - Provide a 1–2 line highlight of the main topic of the video and the specific area it best covers.\n",
    "    - Format:\n",
    "        - Domain (e.g., Finance, Health, Technology, Psychology, Fitness, Productivity, etc.)\n",
    "        - Sub-area (e.g., investment strategies, portfolio design; training routine, best exercises; productivity systems, cognitive science insights, etc.)\n",
    "\n",
    "    3. Summary of the Video\n",
    "    - A structured, clear, and concise summary of the video.\n",
    "    - Focus only on relevant, high-value content.\n",
    "    - Skip fluff, tangents, product promotions, personal banter, or irrelevant side discussions.\n",
    "    - Include key insights, frameworks, step-by-step methods, and actionable advice.\n",
    "    - Where applicable, reference scientific studies, historical sources, or authoritative references (with author + year or journal if mentioned in the video, or inferred if the reference is well known).\n",
    "\n",
    "    Style & Quality Rules:\n",
    "    - Be extremely specific: avoid vague generalizations.\n",
    "    - Use precise language and structured formatting (bullet points, numbered lists, sub-sections if needed).\n",
    "    - Prioritize clarity and factual accuracy.\n",
    "    - Write as though preparing an executive briefing or academic digest.\n",
    "    - If the transcript includes non-relevant sections (jokes, ads, unrelated chit-chat), skip summarizing them entirely.\n",
    "    \"\"\"\n",
    "\n",
    "def generate_user_prompt(website, transcript_chunk=None):\n",
    "    \"\"\"Generate user prompt for video summarization\"\"\"\n",
    "    if transcript_chunk:\n",
    "        return f\"\"\"Here is a portion of a YouTube video transcript. Use the system instructions to generate a summary of this section.\n",
    "\n",
    "    Video Title: {website.title}\n",
    "\n",
    "    Transcript Section: {transcript_chunk}\n",
    "    \"\"\"\n",
    "    else:\n",
    "        return f\"\"\"Here is the transcript of a YouTube video. Use the system instructions to generate the output.\n",
    "\n",
    "    Video Title: {website.title}\n",
    "\n",
    "    Transcript: {website.transcript}\n",
    "    \"\"\"\n",
    "\n",
    "def generate_stitching_prompt(chunk_summaries, video_title):\n",
    "    \"\"\"Generate prompt for stitching together chunk summaries\"\"\"\n",
    "    return f\"\"\"You are an expert at combining multiple summaries into a cohesive, comprehensive summary.\n",
    "\n",
    "    Video Title: {video_title}\n",
    "\n",
    "    Below are summaries of different sections of this video. Combine them into a single, well-structured summary that:\n",
    "    1. Maintains the original structure and quality standards\n",
    "    2. Eliminates redundancy between sections\n",
    "    3. Creates smooth transitions between topics\n",
    "    4. Preserves all important information \n",
    "    5. Maintains the academic, professional tone\n",
    "    6. Include examples and nuances where relevant\n",
    "    7. Include the citations and references where applicable\n",
    "\n",
    "    Section Summaries:\n",
    "    {chr(10).join([f\"Section {i+1}: {summary}\" for i, summary in enumerate(chunk_summaries)])}\n",
    "\n",
    "    Please provide a unified, comprehensive summary following the same format as the individual sections.\n",
    "    Make sure the final summary is cohesive and logical.\n",
    "    \"\"\"\n",
    "\n",
    "print(\"✅ Prompt generation functions defined\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c9a620d",
   "metadata": {},
   "source": [
    "## 6. Summarization Functions\n",
    "\n",
    "Core functions for summarizing videos with support for both single-chunk and chunked processing.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cc8a183b",
   "metadata": {},
   "outputs": [],
   "source": [
    "def summarize_single_chunk(website, client):\n",
    "    \"\"\"Summarize a single chunk (small video)\"\"\"\n",
    "    system_prompt = generate_system_prompt()\n",
    "    user_prompt = generate_user_prompt(website)\n",
    "    \n",
    "    try:\n",
    "        response = client.chat.completions.create(\n",
    "            model=\"gpt-4o-mini\",\n",
    "            messages=[\n",
    "                {\"role\": \"system\", \"content\": system_prompt},\n",
    "                {\"role\": \"user\", \"content\": user_prompt}\n",
    "            ],\n",
    "            max_tokens=2000,\n",
    "            temperature=0.3\n",
    "        )\n",
    "        \n",
    "        return response.choices[0].message.content\n",
    "        \n",
    "    except Exception as e:\n",
    "        return f\"Error generating summary: {str(e)}\"\n",
    "\n",
    "def summarize_with_chunking(website, client, max_chunk_tokens=4000):\n",
    "    \"\"\"Summarize a large video by chunking and stitching\"\"\"\n",
    "    print(\"Video is large, using chunking strategy...\")\n",
    "    \n",
    "    # Chunk the transcript\n",
    "    chunks = chunk_transcript(website.transcript, max_chunk_tokens)\n",
    "    print(f\"Split into {len(chunks)} chunks\")\n",
    "    \n",
    "    # Summarize each chunk\n",
    "    chunk_summaries = []\n",
    "    system_prompt = generate_system_prompt()\n",
    "    \n",
    "    for i, chunk in enumerate(chunks):\n",
    "        print(f\"Processing chunk {i+1}/{len(chunks)}...\")\n",
    "        user_prompt = generate_user_prompt(website, chunk)\n",
    "        \n",
    "        try:\n",
    "            response = client.chat.completions.create(\n",
    "                model=\"gpt-4o-mini\",\n",
    "                messages=[\n",
    "                    {\"role\": \"system\", \"content\": system_prompt},\n",
    "                    {\"role\": \"user\", \"content\": user_prompt}\n",
    "                ],\n",
    "                max_tokens=1500,  # Smaller for chunks\n",
    "                temperature=0.3\n",
    "            )\n",
    "            \n",
    "            chunk_summaries.append(response.choices[0].message.content)\n",
    "            \n",
    "        except Exception as e:\n",
    "            chunk_summaries.append(f\"Error in chunk {i+1}: {str(e)}\")\n",
    "    \n",
    "    # Stitch the summaries together\n",
    "    print(\"Stitching summaries together...\")\n",
    "    stitching_prompt = generate_stitching_prompt(chunk_summaries, website.title)\n",
    "    \n",
    "    try:\n",
    "        response = client.chat.completions.create(\n",
    "            model=\"gpt-4o-mini\",\n",
    "            messages=[\n",
    "                {\"role\": \"system\", \"content\": \"You are an expert at combining multiple summaries into a cohesive, comprehensive summary.\"},\n",
    "                {\"role\": \"user\", \"content\": stitching_prompt}\n",
    "            ],\n",
    "            max_tokens=2000,\n",
    "            temperature=0.3\n",
    "        )\n",
    "        \n",
    "        return response.choices[0].message.content\n",
    "        \n",
    "    except Exception as e:\n",
    "        return f\"Error stitching summaries: {str(e)}\"\n",
    "\n",
    "print(\"✅ Summarization functions defined\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "99168160",
   "metadata": {},
   "outputs": [],
   "source": [
    "def summarize_video(website, use_chunking=True, max_chunk_tokens=4000):\n",
    "    \"\"\"Summarize a YouTube video using OpenAI API with optional chunking for large videos\"\"\"\n",
    "    client = get_openai_client()\n",
    "    \n",
    "    # Check if we need chunking\n",
    "    transcript_text = \" \".join([segment.text for segment in website.transcript])\n",
    "    total_tokens = count_tokens(transcript_text)\n",
    "    \n",
    "    print(f\"Total transcript tokens: {total_tokens}\")\n",
    "    \n",
    "    if total_tokens <= max_chunk_tokens and not use_chunking:\n",
    "        # Single summary for small videos\n",
    "        return summarize_single_chunk(website, client)\n",
    "    else:\n",
    "        # Chunked summary for large videos\n",
    "        return summarize_with_chunking(website, client, max_chunk_tokens)\n",
    "\n",
    "print(\"✅ Main summarization function defined\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "54a76dab",
   "metadata": {},
   "source": [
    "## 7. Interactive Demo\n",
    "\n",
    "Now let's test the YouTube video summarizer with a sample video. You can replace the URL with any YouTube video you want to summarize.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "87badeff",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Example usage - replace with your YouTube URL\n",
    "video_url = \"https://www.youtube.com/watch?v=Xan5JnecLNA\"\n",
    "\n",
    "try:\n",
    "    # Create YouTube video object\n",
    "    print(\"🎬 Fetching video data...\")\n",
    "    video = YouTubeVideo(video_url)\n",
    "    \n",
    "    # Display video info\n",
    "    print(f\"📺 Video Title: {video.title}\")\n",
    "    print(f\"🆔 Video ID: {video.video_id}\")\n",
    "    \n",
    "    # Count tokens in transcript\n",
    "    transcript_text = video.get_transcript_text()\n",
    "    total_tokens = count_tokens(transcript_text)\n",
    "    print(f\"📊 Total transcript tokens: {total_tokens}\")\n",
    "    \n",
    "    # Show video info\n",
    "    info = video.get_video_info()\n",
    "    print(f\"📝 Transcript segments: {info['transcript_length']}\")\n",
    "    \n",
    "except Exception as e:\n",
    "    print(f\"❌ Error: {str(e)}\")\n",
    "    print(\"💡 Make sure the YouTube URL is valid and the video has captions available\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b9e4cf2f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Generate summary (automatically uses chunking if needed)\n",
    "if 'video' in locals():\n",
    "    print(\"\\n🤖 Generating summary...\")\n",
    "    print(\"⏳ This may take a few minutes for long videos...\")\n",
    "    \n",
    "    try:\n",
    "        summary = summarize_video(video, use_chunking=True, max_chunk_tokens=4000)\n",
    "        \n",
    "        # Display results with nice formatting\n",
    "        print(\"\\n\" + \"=\"*60)\n",
    "        print(\"📋 FINAL SUMMARY\")\n",
    "        print(\"=\"*60)\n",
    "        \n",
    "        # Use IPython display if available for better formatting\n",
    "        if display and Markdown:\n",
    "            display(Markdown(summary))\n",
    "        else:\n",
    "            print(summary)\n",
    "            \n",
    "    except Exception as e:\n",
    "        print(f\"❌ Error generating summary: {str(e)}\")\n",
    "else:\n",
    "    print(\"⚠️ Please run the previous cell first to load a video\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42ff8a15",
   "metadata": {},
   "source": [
    "## 8. Testing and Utility Functions\n",
    "\n",
    "Additional functions for testing the chunking functionality and other utilities.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d798b08f",
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_chunking():\n",
    "    \"\"\"Test function to demonstrate chunking with a sample transcript\"\"\"\n",
    "    # Sample transcript for testing\n",
    "    sample_transcript = [\n",
    "        {\"text\": \"This is a sample transcript segment 1. \" * 100},  # ~1000 tokens\n",
    "        {\"text\": \"This is a sample transcript segment 2. \" * 100},  # ~1000 tokens\n",
    "        {\"text\": \"This is a sample transcript segment 3. \" * 100},  # ~1000 tokens\n",
    "        {\"text\": \"This is a sample transcript segment 4. \" * 100},  # ~1000 tokens\n",
    "        {\"text\": \"This is a sample transcript segment 5. \" * 100},  # ~1000 tokens\n",
    "    ]\n",
    "    \n",
    "    print(\"🧪 Testing chunking functionality...\")\n",
    "    chunks = chunk_transcript(sample_transcript, max_tokens=2000, overlap_tokens=100)\n",
    "    \n",
    "    print(f\"📊 Original transcript: {count_tokens(' '.join([s['text'] for s in sample_transcript]))} tokens\")\n",
    "    print(f\"📦 Number of chunks: {len(chunks)}\")\n",
    "    \n",
    "    for i, chunk in enumerate(chunks):\n",
    "        print(f\"📄 Chunk {i+1}: {count_tokens(chunk)} tokens\")\n",
    "\n",
    "def analyze_video_tokens(video_url):\n",
    "    \"\"\"Analyze token count and chunking strategy for a video\"\"\"\n",
    "    try:\n",
    "        video = YouTubeVideo(video_url)\n",
    "        transcript_text = video.get_transcript_text()\n",
    "        total_tokens = count_tokens(transcript_text)\n",
    "        \n",
    "        print(f\"📺 Video: {video.title}\")\n",
    "        print(f\"📊 Total tokens: {total_tokens}\")\n",
    "        print(f\"📦 Optimal chunk size: {get_optimal_chunk_size()}\")\n",
    "        \n",
    "        if total_tokens > 4000:\n",
    "            chunks = chunk_transcript(video.transcript, max_tokens=4000)\n",
    "            print(f\"🔀 Would be split into {len(chunks)} chunks\")\n",
    "            print(\"✅ Chunking strategy recommended\")\n",
    "        else:\n",
    "            print(\"✅ Single summary strategy sufficient\")\n",
    "            \n",
    "    except Exception as e:\n",
    "        print(f\"❌ Error analyzing video: {str(e)}\")\n",
    "\n",
    "print(\"✅ Testing and utility functions defined\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bfd789e5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Test chunking functionality (optional)\n",
    "# Uncomment the line below to test chunking with sample data\n",
    "# test_chunking()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3528125f",
   "metadata": {},
   "source": [
    "## 9. Usage Instructions\n",
    "\n",
    "### How to Use This Notebook\n",
    "\n",
    "1. **Set up your OpenAI API key**:\n",
    "   - Create a `.env` file in the same directory as this notebook\n",
    "   - Add your API key: `OPENAI_API_KEY=your_api_key_here`\n",
    "   - Or set it as an environment variable\n",
    "\n",
    "2. **Install dependencies**:\n",
    "   ```bash\n",
    "   pip install -r requirements.txt\n",
    "   ```\n",
    "\n",
    "3. **Run the cells in order**:\n",
    "   - Start with the import and setup cells\n",
    "   - Modify the `video_url` variable in the demo section\n",
    "   - Run the demo cells to test the summarizer\n",
    "\n",
    "### Customization Options\n",
    "\n",
    "- **Change the model**: Modify the model parameter in the summarization functions\n",
    "- **Adjust chunk size**: Change `max_chunk_tokens` parameter\n",
    "- **Modify prompts**: Edit the prompt generation functions for different output styles\n",
    "- **Add error handling**: Extend the exception handling as needed\n",
    "\n",
    "### Features\n",
    "\n",
    "- ✅ **Automatic transcript extraction** from YouTube videos\n",
    "- ✅ **Intelligent chunking** for videos exceeding token limits\n",
    "- ✅ **Academic-quality summaries** with structured output\n",
    "- ✅ **Error handling** and dependency validation\n",
    "- ✅ **Interactive testing** with sample data\n",
    "- ✅ **Token analysis** and optimization recommendations\n",
    "\n",
    "### Troubleshooting\n",
    "\n",
    "- **\"No transcript available\"**: The video may not have captions enabled\n",
    "- **\"Invalid YouTube URL\"**: Make sure the URL follows the correct format\n",
    "- **\"API key not set\"**: Check your `.env` file or environment variables\n",
    "- **Import errors**: Run `pip install -r requirements.txt` to install dependencies\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5a44fb8",
   "metadata": {},
   "source": [
    "## 10. Advanced Usage Examples\n",
    "\n",
    "Here are some advanced usage patterns you can try with this notebook.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2bef390a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Example 1: Analyze multiple videos\n",
    "video_urls = [\n",
    "    \"https://www.youtube.com/watch?v=Xan5JnecLNA\",\n",
    "    # Add more URLs here\n",
    "]\n",
    "\n",
    "for url in video_urls:\n",
    "    print(f\"\\n{'='*50}\")\n",
    "    print(f\"Analyzing: {url}\")\n",
    "    print('='*50)\n",
    "    analyze_video_tokens(url)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fbdb5cd8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Example 2: Custom summarization with different parameters\n",
    "def custom_summarize(video_url, model=\"gpt-4o-mini\", max_tokens=3000, temperature=0.1):\n",
    "    \"\"\"Custom summarization with specific parameters\"\"\"\n",
    "    try:\n",
    "        video = YouTubeVideo(video_url)\n",
    "        client = get_openai_client()\n",
    "        \n",
    "        # Use custom chunking parameters\n",
    "        chunks = chunk_transcript(video.transcript, max_tokens=max_tokens)\n",
    "        \n",
    "        if len(chunks) == 1:\n",
    "            # Single chunk\n",
    "            system_prompt = generate_system_prompt()\n",
    "            user_prompt = generate_user_prompt(video, chunks[0])\n",
    "            \n",
    "            response = client.chat.completions.create(\n",
    "                model=model,\n",
    "                messages=[\n",
    "                    {\"role\": \"system\", \"content\": system_prompt},\n",
    "                    {\"role\": \"user\", \"content\": user_prompt}\n",
    "                ],\n",
    "                max_tokens=2000,\n",
    "                temperature=temperature\n",
    "            )\n",
    "            \n",
    "            return response.choices[0].message.content\n",
    "        else:\n",
    "            # Multiple chunks - use standard chunking approach\n",
    "            return summarize_with_chunking(video, client, max_tokens)\n",
    "            \n",
    "    except Exception as e:\n",
    "        return f\"Error: {str(e)}\"\n",
    "\n",
    "# Example usage:\n",
    "# custom_summary = custom_summarize(\"https://www.youtube.com/watch?v=Xan5JnecLNA\")\n",
    "# print(custom_summary)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f7a5a9e9",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Generate summary (automatically uses chunking if needed)\n",
    "if 'video' in locals():\n",
    "    print(\"\\n🤖 Generating summary...\")\n",
    "    print(\"⏳ This may take a few minutes for long videos...\")\n",
    "    \n",
    "    try:\n",
    "        summary = summarize_video(video, use_chunking=True, max_chunk_tokens=4000)\n",
    "        \n",
    "        # Display results with nice formatting\n",
    "        print(\"\\n\" + \"=\"*60)\n",
    "        print(\"📋 FINAL SUMMARY\")\n",
    "        print(\"=\"*60)\n",
    "        \n",
    "        # Use IPython display if available for better formatting\n",
    "        if display and Markdown:\n",
    "            display(Markdown(summary))\n",
    "        else:\n",
    "            print(summary)\n",
    "            \n",
    "    except Exception as e:\n",
    "        print(f\"❌ Error generating summary: {str(e)}\")\n",
    "else:\n",
    "    print(\"⚠️ Please run the previous cell first to load a video\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4028fa5e",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c100b384-2c3e-49de-92ce-f5dd0b4b58c0",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
